<script setup lang="ts">
import { LockOutlined, UserOutlined } from '@ant-design/icons-vue';
import { reactive } from 'vue';
import { useRoute } from 'vue-router';

import router from '../app-router';
import FormItemHelper, { FormItemValidationResultMapping } from '../shared/models/form-item-helper';
import Token from '../shared/models/token';
import AuthenticationService from '../shared/services/authentication-service';
import FormTextFieldValidator from '../shared/services/form-text-field-validator';
import RememberMeService from '../shared/services/remember-me-service';
import TokenService from '../shared/services/token-service';
import useAuthenticationStore from '../shared/stores/authentication-store';
import User from '../user/user';
import AuthService from './auth-service';

const route = useRoute();
const authenticationStore = useAuthenticationStore();
const formName = 'form';

const validationResultMapping = reactive<FormItemValidationResultMapping>({
  username: {
    name: 'username',
    label: '用户名',
  },
  password: {
    name: 'password',
    label: '密码',
  },
});

const formState = reactive({
  username: authenticationStore.currentUsername,
  password: '',
  rememberMe: RememberMeService.rememberMe,
});

async function login(username: string, password: string, rememberMe: boolean): Promise<void> {
  const res = await AuthService.login(username, password);

  if (res?.accessToken) {
    RememberMeService.rememberMe = rememberMe;

    // Gets the server data to user instance.
    const user = User.fromJSON(res);

    // Saves the user data retrieved from server.
    AuthenticationService.saveUserInfo(user);

    // Saves current username for next logic form auto fill input.
    AuthenticationService.saveCurrentUsername();

    // Gets the server data to token instance.
    const token = await Token.fromServerJson(res);

    // Saves the token data retrieved from server.
    TokenService.saveToken(token);

    // Notifies change.
    authenticationStore.changeIsAuthenticated(AuthenticationService.isAuthenticated);
    authenticationStore.changeCurrentUser(AuthenticationService.currentUser);
    authenticationStore.changeCurrentUsername(AuthenticationService.currentUsername);

    const redirectTo = route.query.redirectTo?.toString() ?? '/home';

    // Once user logged in the login route will be replaced in the history stack.
    router.replace({ path: redirectTo })
  } else {
    validationResultMapping.password = Object.assign(validationResultMapping.password, FormItemHelper.getErrorValidationResult('用户名或密码错误'));
  }
};

/**
 * Handles input value change for validation purpose.
 */
function handleChange(e: any): void {
  // The id of input under form-item is auto-assigned. It will be started with form name. That why we need to remove it.
  const key = e.target.id.replace(`${formName}_`, '');
  const value = e.target.value;

  const validator = new FormTextFieldValidator(validationResultMapping[key].validatorArguments);
  validator.args.label = validationResultMapping[key].label!;
  validator.args.value = value;

  // Merges object changes.
  Object.assign(validationResultMapping[key], FormItemHelper.validate(validator));
}

/**
 * When form submitted.
 */
function onFinish(values: any): void {
  validateForm(values);

  if (FormItemHelper.isFormValidationSuccessful(validationResultMapping)) {
    login(values.username, values.password, values.rememberMe);
  }
};

function validateForm(values: any): void {
  for (const key in validationResultMapping) {
    const validator = new FormTextFieldValidator(validationResultMapping[key].validatorArguments);
    validator.args.label = validationResultMapping[key].label!;
    validator.args.value = values[validationResultMapping[key].name!];

    // Merges two objects.  
    Object.assign(validationResultMapping[key], FormItemHelper.validate(validator));
  }
}
</script>

<template>
  <div class="container">
    <div class="form-container">
      <h2 class="title">平台管理系统</h2>
      <a-form :name="formName" :model="formState" autocomplete="off" @finish="onFinish">
        <a-form-item :name="validationResultMapping.username.name"
          :validateStatus="validationResultMapping.username.validateStatus"
          :help="validationResultMapping.username.errorMsg">
          <a-input v-model:value="formState.username" :placeholder="validationResultMapping.username.label"
            @change="handleChange">
            <template #prefix>
              <UserOutlined class="site-form-item-icon" />
            </template>
          </a-input>
        </a-form-item>
        <a-form-item :name="validationResultMapping.password.name"
          :validateStatus="validationResultMapping.password.validateStatus"
          :help="validationResultMapping.password.errorMsg">
          <a-input-password v-model:value="formState.password" :placeholder="validationResultMapping.password.label"
            @change="handleChange">
            <template #prefix>
              <LockOutlined class="site-form-item-icon" />
            </template>
          </a-input-password>
        </a-form-item>
        <a-form-item name="rememberMe">
          <a-checkbox v-model:checked="formState.rememberMe">记住我</a-checkbox>
        </a-form-item>
        <a-form-item>
          <a-button type="primary" html-type="submit" class="form-button">登录</a-button>
        </a-form-item>
      </a-form>
    </div>
  </div>
</template>

<style scoped>
.container {
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: #fff;
}

.title {
  margin-top: 6px;
  align-self: center;
  margin-bottom: 10px;
}

.form-container {
  display: flex;
  flex-direction: column;
  width: 400px;
  background-color: #dcdcdc;
  margin-top: 180px;
  padding: 10px;
}

.form-button {
  width: 100%;
}
</style>